#include "number_vector.h"

#include <cstdlib>
#include <cstring>
#include <sstream>
#include <utility>

/*
 * ================================================================
 * class NumberVector::iterator
 * ================================================================
 */

NumberVector::iterator::iterator(NumberVector &num_vec, int ind) : container(num_vec), ind(ind) {}

NumberVector::iterator::iterator(const iterator &other) : container(other.container), ind(other.ind) {}

NumberVector::iterator & NumberVector::iterator::operator=(const iterator &other) {
    if(this == &other) return *this;

    this->container = other.container;
    this->ind = other.ind;
    return *this;
}

NumberVector::iterator::reference NumberVector::iterator::operator*() const {
    if(this->ind != -1) return this->container.nums[this->ind];
    else throw std::exception();
}

NumberVector::iterator::pointer NumberVector::iterator::operator->() {
    if(this->ind != -1) return this->container.nums + ind;
    else return NULL;
}

NumberVector::iterator & NumberVector::iterator::operator++() {
    if(ind == this->container.size() - 1) ind = -1;
    else if(ind != -1) ind++;
    return *this;
}

NumberVector::iterator NumberVector::iterator::operator++(int) {
    iterator res = *this;
    ++(*this);
    return res;
}

NumberVector::iterator & NumberVector::iterator::operator--() {
    if(ind == -1) ind = this->container.size() - 1;
    else if(ind != 0) ind--;
    return *this;
}

NumberVector::iterator NumberVector::iterator::operator--(int) {
    iterator res = *this;
    --(*this);
    return res;
}


NumberVector::iterator NumberVector::iterator::operator+(int a) {
    int new_ind = (this->ind == -1) ? this->container.size() : this->ind;
    new_ind += a;
    if(new_ind < 0) new_ind = 0;
    else if(new_ind >= this->container.size()) new_ind = -1;
    return iterator(this->container, new_ind);
}


NumberVector::iterator operator+(int a, NumberVector::iterator &it) {
    return it + a;
}


NumberVector::iterator NumberVector::iterator::operator-(int a) {
    iterator res(*this);
    return res + (-a);
}

NumberVector::iterator::difference_type NumberVector::iterator::operator-(const iterator &other) {
    int ind1 = (this->ind == -1) ? this->container.size() : this->ind,
        ind2 = (other.ind == -1) ? other.container.size() : other.ind;
    return abs(ind1 - ind2);
}

NumberVector::iterator& NumberVector::iterator::operator+=(int a) {
    *this = *this + a;
    return *this;
}

NumberVector::iterator& NumberVector::iterator::operator-=(int a) {
    *this = *this - a;
    return *this;
}

NumberVector::iterator::reference NumberVector::iterator::operator[](int ind) {
    return *(*this + ind);
}

bool NumberVector::iterator::operator==(const iterator &other) {
    return &(this->container) == &(other.container) && this->ind == other.ind;
}

bool NumberVector::iterator::operator!=(const iterator &other) {
    return !(*this == other);
}

bool NumberVector::iterator::operator<(const iterator &other) {
    return other.ind == -1 && this->ind != -1 || this->ind != 1 && this->ind < other.ind;
}

bool NumberVector::iterator::operator>(const iterator &other) {
    return !(*this < other || *this == other);
}

bool NumberVector::iterator::operator<=(const iterator &other) {
    return *this < other || *this == other;
}

bool NumberVector::iterator::operator>=(const iterator &other) {
    return *this > other || *this == other;
}

/*
 * ================================================================
 * class NumberVector
 * ================================================================
 */

/*
 * Constructors & destructor
 */

NumberVector::NumberVector() {
    this->nums = static_cast<int*>(std::malloc(0 * sizeof(int)));
    this->_size = 0;
}

NumberVector::NumberVector(int n) {
    this->nums = static_cast<int*>(std::malloc(n * sizeof(int)));
    std::memset(this->nums, 0, n * sizeof(int));
    this->_size = n;
}

NumberVector::NumberVector(int n, int a) {
    this->nums = static_cast<int*>(std::malloc(n * sizeof(int)));
    for(int i=0; i<n; i++) this->nums[i] = a;
    this->_size = n;
}

NumberVector::NumberVector(std::initializer_list<int> init) {
    this->nums = static_cast<int*>(std::malloc(init.size() * sizeof(int)));
    int *ptr = this->nums;
    for(int el: init) { *ptr = el; ptr++; }
    this->_size = init.size();
}

NumberVector::NumberVector(const NumberVector &other) {
    this->nums = static_cast<int*>(std::malloc(other.size() * sizeof(int)));
    std::memcpy(this->nums, other.nums, other.size() * sizeof(int));
    this->_size = other.size();
}

NumberVector::~NumberVector() {
    std::free(this->nums);
}

/*
 * Overloads
 */

NumberVector & NumberVector::operator=(const NumberVector &other) {
    if(this == &other) return *this;

    this->nums = static_cast<int*>(std::malloc(other.size() * sizeof(int)));
    std::memcpy(this->nums, other.nums, other.size() * sizeof(int));
    this->_size = other._size;
    return *this;
}

NumberVector & NumberVector::operator=(NumberVector &&other) noexcept {
    if(this == &other) return *this;

    this->nums = static_cast<int*>(std::malloc(other.size() * sizeof(int)));
    std::memcpy(this->nums, other.nums, other.size() * sizeof(int));
    other.nums = static_cast<int*>(std::realloc(other.nums, 0));
    this->_size = std::exchange(other._size, 0);

    return *this;
}

int & NumberVector::operator[](int ind) {
    if(ind >= this->size() || ind < 0) throw std::out_of_range("Index is negative or greater than or equal to size");
    return this->nums[ind];
}

int NumberVector::operator()() {
    return this->size();
}

NumberVector NumberVector::operator*(int mult) {
    NumberVector res = *this;
    for(int i=0; i<this->_size; i++) res.nums[i] *= mult;
    return res;
}

std::ostream & operator<<(std::ostream &os, const NumberVector &num_vec) {
    if(num_vec._size == 0) return os;
    os << num_vec.nums[0];
    for(int i=1; i<num_vec._size; i++) os << ' ' << num_vec.nums[i];
    return os;
}

std::istream & operator>>(std::istream &is, NumberVector &num_vec) {
    std::string inp;
    for(;;) {
        std::getline(is, inp);
        if(!strchr(inp.c_str(), ' ') && !strchr(inp.c_str(), '\t') &&
           !strchr(inp.c_str(), '\v') && !strchr(inp.c_str(), '\r') &&
           !inp.empty()) break;
    }

    std::istringstream iss(inp);
    int el, ind = 0, count = 0;
    while(iss >> el) count++;
    num_vec = NumberVector(count);
    iss = std::istringstream(inp);
    while(iss >> el) { num_vec[ind] = el; ind++; }
    return is;
}

/*
 * Public methods (interface)
 */

int NumberVector::size() const {
    return this->_size;
}

NumberVector::iterator NumberVector::begin() {
    return iterator(*this, 0);
}

NumberVector::iterator NumberVector::end() {
    return iterator(*this, -1);
}
